package oneD.fem.structure;

import inf.jlinalg.ArrayVector;
import inf.jlinalg.BLAM;
import inf.jlinalg.IMatrix;
import inf.jlinalg.IVector;

import java.util.ArrayList;

import oneD.fem.Node;
import oneD.fem.element.Element;

public class Structure {
	protected ArrayList<Node> nodes_;
	protected ArrayList<Element> elements_;
	protected int[] dofNumbers_;
	private int numberOfEquations_;

	public Structure() {
		this.nodes_ = new ArrayList<Node>();
		this.elements_ = new ArrayList<Element>();
		this.dofNumbers_ = null;
	}

	public Node addNode(double x1, double x2, double x3) {
		Node n = new Node(x1, x2, x3);
		this.nodes_.add(n);
		return n;
	}

	public Node addNode(double[] x) {
		Node n = new Node(x[0], x[1], x[2]);
		this.nodes_.add(n);
		return n;
	}

	public void addNode(Node n) {
		this.nodes_.add(n);
	}

	public int getNumberOfElements() {
		return this.elements_.size();
	}

	public Element getElement(int id) {
		return this.elements_.get(id);
	}

	public int getNumberOfNodes() {
		return this.nodes_.size();
	}

	public Node getNode(int id) {
		return this.nodes_.get(id);
	}

	public int[] getDofNumbers_() {
		return this.dofNumbers_;
	}

	public int getNumberOfEquations() {
		return numberOfEquations_;
	}

	public IVector getU() {
		// int numNode = this.getNumberOfNodes();
		// IVector U = new ArrayVector(3 * numNode);
		// for (int i = 0; i < numNode; i++) {
		// double[] ui = this.getNode(i).getDisplacement().toArray();
		// for (int j = 0; j < 3; j++)
		// U.set(3 * i + j, ui[j]);
		// }
		// return U;
		IVector U = new ArrayVector(this.numberOfEquations_);
		int cnt = 0;
		for (int i = 0; i < this.getNumberOfNodes(); i++) {
			int[] dofNumbers = this.getNode(i).getDOFNumbers();
			for (int j = 0; j < 3; j++)
				if (dofNumbers[j] != -1)
					U.set(cnt++, this.getNode(i).getDisplacement().get(j));
		}
		return U;
	}

	public IVector getX() {
		int numNode = this.getNumberOfNodes();
		IVector X = new ArrayVector(3 * numNode);
		for (int i = 0; i < numNode; i++) {
			double[] xi = this.getNode(i).getPosition().toArray();
			for (int j = 0; j < 3; j++)
				X.set(3 * i + j, xi[j]);
		}
		return X;
	}

	public IVector createZeroU() {
		IVector U = new ArrayVector(this.getNumberOfEquations());
		BLAM.fill(U, 0);
		return U;
	}

	public int enumerateDOFs() {
		int start = 0;
		// this.dofNumbers_ = new int[3*this.getNumberOfNodes()];
		for (int i = 0; i < this.getNumberOfNodes(); i++) {
			start = this.nodes_.get(i).enumerateDOFs(start);
			this.nodes_.get(i).setId(i);
			// int [] id = this.nodes_.get(i).getDOFNumbers();
			// this.dofNumbers_[3*i] = id[0];
			// this.dofNumbers_[3*i+1] = id[1];
			// this.dofNumbers_[3*i+2] = id[2];
		}
		for (int i = 0; i < this.getNumberOfElements(); i++) {
			this.elements_.get(i).enumerateDOFs();
			this.elements_.get(i).setId(i);
		}
		this.numberOfEquations_ = start;
		return start;
	}

	public void setDisplacement(IVector uGlobal) {
		/*
		 * uGlobal is displacement vector after constrainted
		 */
		for (int i = 0; i < this.getNumberOfNodes(); i++) {
			double[] u = new double[3];
			int[] dofNumbers = this.nodes_.get(i).getDOFNumbers();
			for (int j = 0; j < 3; j++)
				if (dofNumbers[j] == -1)
					u[j] = 0;
				else
					u[j] = uGlobal.get(dofNumbers[j]);
			this.nodes_.get(i).setDisplacement(u);
		}
	}

	public void setDisplacement(double[] uGlobal) {
		this.setDisplacement(new ArrayVector(uGlobal));
	}

	public void updateDisplacement(double[] duGlobal) {
		this.updateDisplacement(new ArrayVector(duGlobal));
	}

	public void updateDisplacement(IVector duGlobal) {
		for (int i = 0; i < this.getNumberOfNodes(); i++) {
			double[] du = new double[3];
			int[] dofNumbers = this.nodes_.get(i).getDOFNumbers();
			for (int j = 0; j < 3; j++)
				if (dofNumbers[j] == -1)
					du[j] = 0;
				else
					du[j] = duGlobal.get(dofNumbers[j]);
			this.nodes_.get(i).updateDisplacement(du);
		}
	}

	public void printStructure() {
		System.out.println("Listing structure\n");
		System.out.println("Nodes:");
		System.out.println("idx\t\tx1\t\tx2\t\tx3");
		for (int i = 0; i < this.getNumberOfNodes(); i++) {
			System.out.print(i + "\t");
			this.nodes_.get(i).printPosition();
			System.out.println();
		}
		System.out.println("Constraints:");
		System.out.println("node\tu1\tu2\tu3");
		for (int i = 0; i < this.getNumberOfNodes(); i++) {
			System.out.print(i + "\t");
			this.nodes_.get(i).printConstraint();
			System.out.println();
		}
		System.out.println("Forces:");
		System.out.println("node\t\tr1\t\tr2\t\tr3");
		for (int i = 0; i < this.getNumberOfNodes(); i++) {
			if (this.nodes_.get(i).getForce() != null) {
				System.out.print(i + "\t");
				this.nodes_.get(i).getForce().print();
				System.out.println();
			}
		}
	}

	public void printResults() {

	}

	public void assembleInternalForceVector(IVector riGlobal) {

	}

	public void assembleStiffnessMatrix(IMatrix kGlobal, boolean full) {

	}

	public void assembleTangentStiffnessMatrix(IMatrix ktGlobal) {

	}

	public void assembleMassMatrix(IMatrix mGlobal) {

	}

	public void assembleLoadVector(double[] rGlobal) {

	}

	public void assembleLoadVector(IVector rGlobal) {

	}

	public void solve() {

	}

	public void solve(IVector u0) {

	}

	public void secondOrderLoadControlInstabilityComputation(int nev,
			IVector u, IVector lambda) {

	}

	public void directLoadControlInstabilityComputation(IVector u,
			double lambda, IVector phi) {

	}

}
